/*
 * 
 * 
 *
 * 
 */

#include <common.h>
#include <bootretry.h>
#include <cli.h>
#include <command.h>
#include <console.h>
#include <dm.h>
#include <edid.h>
#include <environment.h>
#include <errno.h>
#include <i2c.h>
#include <malloc.h>
#include <asm/byteorder.h>
#include <linux/compiler.h>
#include <pwm.h>
/*↓*↓*↓*↓*↓*↓*↓**nova_lcd_logo**↓*↓*↓*↓*↓*↓*↓*/
#include <spi.h>
#include <asm/gpio.h>
#include <asm/unaligned.h>
#include <bmp_layout.h>
#include <mapmem.h>
#include <part.h>
#include <config.h>
#include <image.h>
#include <linux/ctype.h>
#include <ext4fs.h>
#include <linux/stat.h>
#include <fs.h>
#include <linux/math64.h>
/*↑*↑*↑*↑*↑*↑*↑**nova_lcd_logo**↑*↑*↑*↑*↑*↑*↑*/


extern int crc_flag;

/*LCD驱动部分*/
#define LCD_MAIN     2  /*通过SPI的编号，来决定是哪一个屏幕的，不能随意更改*/
#define LCD_PREVIEW  1  /**/
extern void lcd_init(int spi_num);
void lcd_draw_bmp_pic(unsigned char spi_num,unsigned char *pic);
extern void nova_logo_display_off(unsigned char spi_num);
extern void nova_logo_display_on(unsigned char spi_num);
/*按键检测部分*/
        /*前面板power 按键灯*/
#define POWER_LED_L          run_command("gpio clear H6", 0);    //H6 --RK3288_GPIO7_A6 EXT_POWER_LED
#define POWER_LED_H          run_command("gpio set H6", 0);     
        /*前面板开关机按键*/
#define KEY_DET_READ         run_command("gpio status -a E23", 0);     //E23 --KEY_DET--RK3288_GPIO4_C7
#define KEY_DET_SET_INPUT    run_command("gpio input E23", 0);   //E23 设置为输入
/*LOGO加载部分*/
int logo_load_from_ext4(char *dev_name, char *dev_part, char *dst_addr, char *file_path);
unsigned char *lcd_get_bmp(unsigned long addr);

/*风扇*/
#define FAN_0_12V_PWM     run_command("gpio set F18", 0);//F18 RK3288_GPIO5_C2
#define FAN_1_12V_PWM     run_command("gpio set G2 ", 0);//G2  RK3288_GPIO6_A2
#define FAN_2_12V_PWM     run_command("gpio set G5 ", 0);//G5  RK3288_GPIO6_A5
#define FAN_3_12V_PWM     run_command("gpio set G8 ", 0);//G8  RK3288_GPIO6_B0
#define FAN_4_12V_PWM     run_command("gpio set G0 ", 0);//G0  RK3288_GPIO6_A0
/*按键板电源控制*/
#define KEYPAD_PWR_EN     run_command("gpio set A14", 0);  //A14    PMUGPIO0_B6_d
/*INPUT卡控制*/
#define INPUT_NRST_NC     run_command("gpio clear H10", 0);    //H10    RK3288_GPIO7_B2
#define INPUT_NRST        run_command("gpio set H10", 0);    //H10    RK3288_GPIO7_B2  1216+8+2 1226
#define INPUT_BOOT0       run_command("gpio clear H11", 0);  //H11    RK3288_GPIO7_B3  1216+8+3 1227
#define INPUT_POWER_EN    run_command("gpio set H21", 0);  //H21    RK3288_GPIO7_C5   1216+16+5 1237
#define INPUT_RFU         run_command("gpio set H9 ", 0);  //H9     RK3288_GPIO7_B1
#define INPUT_RFU1        run_command("gpio set G22", 0);  //G22    RK3288_GPIO6_C6
/*OUTPUT卡控制*/
#define OUTPUT_NRST_NC       run_command("gpio clear E21", 0);  //E21    RK3288_GPIO4_C5
#define OUTPUT_NRST       run_command("gpio set E21", 0);  //E21    RK3288_GPIO4_C5
#define OUTPUT_BOOT0      run_command("gpio clear E27", 0);  //E27    RK3288_GPIO4_D3
#define OUTPUT_POWER_EN   run_command("gpio set E28", 0);  //E28    RK3288_GPIO4_D4
#define OUTPUT_RFU        run_command("gpio set E20", 0);  //E20    RK3288_GPIO4_C4
#define OUTPUT_RFU1       run_command("gpio set G21", 0);  //G21    RK3288_GPIO6_C5
/*VPU卡控制*/
#define VPU_NRST_NC          run_command("gpio clear F11", 0);  //F11    RK3288_GPIO5_B3
#define VPU_NRST          run_command("gpio set F11", 0);  //F11    RK3288_GPIO5_B3
#define VPU_BOOT0         run_command("gpio clear C8 ", 0);  //C8     RK3288_GPIO2_B0
#define VPU_POWER_EN      run_command("gpio set C10", 0);  //C10    RK3288_GPIO2_B2
#define VPU_RFU           run_command("gpio set F10", 0);  //F10    RK3288_GPIO5_B2
#define VPU_RFU1          run_command("gpio set E24", 0);  //E24    RK3288_GPIO4_D0


/*主函数hdr_driver主函数*/
int do_hdr_driver(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	return 0;
  char *pstr = NULL;
  //int env_variable_value;
  //int reboot_enable;
  printf("------------------------\n");
  printf("run in \"do_hdr_driver\"!\n");
  printf("------------------------\n");
  
	/*按键板电源控制*/
    KEYPAD_PWR_EN;  
	POWER_LED_L;
	
	/*2.液晶初始化*/
	lcd_init(LCD_MAIN);
  lcd_init(LCD_PREVIEW);

  /*关闭显示*/
  nova_logo_display_off(LCD_MAIN);
  nova_logo_display_off(LCD_PREVIEW);
  /*关闭fan*/
  printf("close fan!!!\n");
    run_command("gpio clear F18", 0);
    run_command("gpio clear G0", 0);
    run_command("gpio clear G2", 0);
    run_command("gpio clear G5", 0);
    run_command("gpio clear G8", 0);

  /*4.
    环境变量，起始可以认为是一个带等号的字符串
    读取logo的环境变量:  其放置的是一串 run_command 能够认识的命令字符串。
  */
	pstr = getenv("logocmd");// ~~/include/configs/firefly-rk3288.h
	if(pstr) {
	  if(run_command(pstr, 0)){/*使用run_command运行之, 如果运行失败,则手动运行加载代码*/
		  printf("cmd=(%pstr) run error!!\n", pstr);
			logo_load_from_ext4("mmc", "0:7", "0x04000000", "/usr/nova/etc/logo/logo.bmp");
			printf("logo load by logo_load_from_ext4!!\n");
		}else /*已加载LOGO到DDR*/
		{
		  logo_load_from_ext4("mmc", "0:7", "0x04000000", "/usr/nova/etc/logo/logo.bmp");
  		printf("OK！  logo load by logo_load_from_ext4!!\n");
		}
    /*5.LCD的RAM 加载logo图像（暂不显示） */
    //lcd_clear(LCD_MAIN);
    lcd_draw_bmp_pic(LCD_MAIN,lcd_get_bmp(0x04000000));
    lcd_draw_bmp_pic(LCD_PREVIEW,lcd_get_bmp(0x04000000));
	} else {
	  printf("logocmd is Null\n");
	}

	/*1.上电后的IO操作，实现IO控制*/

    /*INPUT卡控制*/
    INPUT_NRST_NC; 
    mdelay(5);
    INPUT_POWER_EN;         
    INPUT_BOOT0; 
    INPUT_RFU;      
    INPUT_RFU1;  
    INPUT_NRST;      
    /*OUTPUT卡控制*/
    OUTPUT_NRST_NC; 
    mdelay(5);
    OUTPUT_POWER_EN;         
    OUTPUT_BOOT0; 
    OUTPUT_RFU;      
    OUTPUT_RFU1;  
    OUTPUT_NRST;     
    /*VPU卡控制*/
    VPU_NRST_NC; 
    mdelay(5);
    VPU_POWER_EN;         
    VPU_BOOT0; 
    VPU_RFU;      
    VPU_RFU1;  
    VPU_NRST;
  
  /*读取reboot标志,确定系统是否是是重新启动的*/
#if 0
  pstr = getenv("nova_reboot");
	reboot_enable = (int)simple_strtol(pstr, NULL, 10);
	if(reboot_enable == 1) {
		/* 5. 检测powerkey是否按下 */
		pstr = getenv("powerkey");// ~~/include/configs/firefly-rk3288.h
		env_variable_value = (int)simple_strtol(pstr, NULL, 10);
		if( (env_variable_value==1) || (env_variable_value==2) ) {
  		
  		int ret;
      unsigned int gpio;
      char  *str_gpio = "E23";/*电源键是E23管脚*/
      
			// // 5.1 设置当前I2C总线为keypad所在的bus2
			// printf("I2C:   ");
			// run_command("i2c dev 2", 0);/*选择I2C总线2*/
			// // 5.2 初始化按键灯
			// led_pwm_init ();
			// led_gpio_init();
			// 5.3 循环检测powerkey是否按下
      /*等待开机按键按下*/
    	ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio);/*通过string 查找GPIO 索引*/
    	if (ret) {
    		printf("GPIO: '%s' not found\n", str_gpio);
    	}
    	ret = gpio_request(gpio, "****");/*请求控制gpio*/
    	if (ret && ret != -EBUSY) {
    		printf("gpio: requesting pin %u failed\n", gpio);
    	}
    	gpio_direction_input(gpio);  /*设置为输入*/
    	printf("waitting for powerkey ...\n");
      printf("key power value is 0x%02x\n",gpio_get_value(gpio));/*读取GPIO 电平值*/
    	do {
    		mdelay(100);
    	}while(gpio_get_value(gpio));/*等待按键按下，然后才能开机*/
   

//                 /*在读秒之前执行用户的自定义命令*/
// #if defined(FEATURE_SUPPORT_HDR_FOR_LINUX)
//   printf("\n");
//   printf("\n");
//   printf("\n");
//   printf("\n");
//   printf("***hdr_driver running in cmd/pxe.c p632***\n");
//   run_command("hdr_driver",0);
//   printf("\n");
//   printf("\n");
//   printf("\n");
//   printf("\n");
// #endif 

			// // 5.4 电源灯亮（红）
			// led_on(0,0,0xffff);
			
			// 5.5 前面板按键灯整体闪烁：红->黄->绿
			// pstr = getenv("powerkey");
			// env_variable_value = (int)simple_strtol(pstr, NULL, 10);
			// if(env_variable_value == 2) {
			// 	keypad_led_display();
			// }

		}
	}else{
		printf("System reboot ...\n");
	}
#endif

  /*7.开启显示*/
  nova_logo_display_on(LCD_MAIN);
  nova_logo_display_on(LCD_PREVIEW);
  
	// /*power*/
	// run_command("gpio set C10", 0);
	// run_command("gpio set H21", 0);
	// run_command("gpio set E28", 0);
	// /*boot*/
	// run_command("gpio set C8", 0);
	// run_command("gpio set H11", 0);
	// run_command("gpio set E27", 0);
	// /*rst*/
	// run_command("gpio set F11", 0);
	// run_command("gpio set H10", 0);
	// run_command("gpio set E21", 0);

#if 0
  /*1.上电后的IO操作，实现IO控制*/
    /*风扇控制*/
    /*按键板电源控制*/
    KEYPAD_PWR_EN;  
    /*INPUT卡控制*/
    INPUT_NRST_NC; 
    mdelay(5);
    INPUT_POWER_EN;         
    INPUT_BOOT0; 
    INPUT_RFU;      
    INPUT_RFU1;  
    INPUT_NRST;      
    /*OUTPUT卡控制*/
    OUTPUT_NRST_NC; 
    mdelay(5);
    OUTPUT_POWER_EN;         
    OUTPUT_BOOT0; 
    OUTPUT_RFU;      
    OUTPUT_RFU1;  
    OUTPUT_NRST;     
    /*VPU卡控制*/
    VPU_NRST_NC; 
    mdelay(5);
    VPU_POWER_EN;         
    VPU_BOOT0; 
    VPU_RFU;      
    VPU_RFU1;  
    VPU_NRST;      
#endif




	
  /* 9. 保存当前环境变量 */
	/* 2018-1-30 15:06:11在这里每次启动都会执行一次saveenv，所以不合适，现已放到/common/env_common.c文件
	下，位置在第112行，set_default_env函数中 ，只有当CRC校验有问题时，才保存*/
	/* 2018-2-2 14:07:20但是测试发现env_common执行时，环境变量还不全，此时保存会导致环境变量丢失，所以现在修改为，
	在env_common.c中若检测到CRC校验问题时，设定一个crc_flag的全局变量，当检测到此环境变量时
	则执行saveenv */
	if (crc_flag == 1) {
		run_command("saveenv",0);
		crc_flag = 0;
		printf("saveenv is DONE!\n");
	}
  return 0;
}


/******************************************************************************
 *函数名：lcd_get_bmp
 *参  数：ulong addr:内存中存放图片的地址
 *返回值：返回bmp图片的数据的地址
 *作  用：去掉bmp的头信息，过去图片数据
 ******************************************************************************/
unsigned char *lcd_get_bmp(unsigned long addr) {
	unsigned char *bmap;
	struct bmp_image *bmp = map_sysmem(addr, 0);
	addr = map_to_sysmem(bmp);
	bmp = (struct bmp_image *)map_sysmem(addr, 0);
	bmap = (unsigned char *)bmp + get_unaligned_le32(&bmp->header.data_offset);

	printf("bmp bit deep is %d bit \n", bmp->header.bit_count);/*打印bmp的图片为深*/
	
	//printf("lcd_get_bmp is ok!\n");
	return bmap;
}
/*从ext4的中加载logo*/
int logo_load_from_ext4(char *dev_name, char *dev_part, char *dst_addr, char *file_path) {
	unsigned long addr;
	const char *addr_str;
	const char *filename;
	loff_t bytes;
	loff_t pos;
	loff_t len_read;
	int ret;
	unsigned long time;
	char *ep;

	if (fs_set_blk_dev(dev_name, dev_part, FS_TYPE_EXT))
		return 1;

	if (5 >= 4) {
		addr = simple_strtoul(dst_addr, &ep, 16);
		if (ep == dst_addr || *ep != '\0')
			return CMD_RET_USAGE;
	} else {
		addr_str = getenv("loadaddr");
		if (addr_str != NULL)
			addr = simple_strtoul(addr_str, NULL, 16);
		else
			addr = CONFIG_SYS_LOAD_ADDR;
	}
	filename = file_path;
	bytes = 0;
	pos = 0;
	time = get_timer(0);
	ret = fs_read(filename, addr, pos, bytes, &len_read);
	time = get_timer(time);
	if (ret < 0)
		return 1;

	printf("%llu bytes read in %lu ms", len_read, time);
	if (time > 0) {
		puts(" (");
		print_size(div_u64(len_read, time) * 1000, "/s");
		puts(")");
	}
	puts("\n");

	setenv_hex("fileaddr", addr);
	setenv_hex("filesize", len_read);
	return 0;
}





/*------------------------------------------------------------------------------
 *命令整体说明：
 *	一、该命令默认在代码中直接运行。
 *		运行位置：uboot/cmd/pxe.c
 *		运行时机：在uboot启动读秒之后（为维护人员提供后门，以便开关该命令）
 *		运行现象：读秒后会在启动kernel前热等待，直到检测到电源键动作。
 *				  执行完命令功能后，继续启动kernel。做到伪开机状态。
 * 	二、执行nova_driver命令时，进入主函数后
 *		a. 首先给前面板上电
 *		b. 预加载logo图像，但是暂不显示
 * 		c. 紧接着检测powerkey环境变量值。
 * 			1. 当powerkey=1时：
 *			   	循环检测电源键是否动作，当检测到动作时，电源键亮红灯
 *			2. 当powerkey=2时：
 *			   	循环检测电源键是否动作，当检测到动作时，电源键亮红灯且键板整体闪灯
 * 			3. 当没有检测到powerkey环境变量或powerkey值不为1或2时：
 *				不检测电源键动作。
 *		d. 无论是否有环境变量，都会执行logo显示动作。
 *		e. 无论是否有环境变量，都会执行各种卡上电动作。
 *		f. 无论是否有环境变量，都会启动风扇（默认最高速）。
 *----------------------------------------------------------------------------*/
 
U_BOOT_CMD(hdr_driver, 2, 0, do_hdr_driver,
	"Enable powerkey , display logo on lcd and power on xxxxcards\n",
	"<powerkey|logo|cardpower|fan> <nova>\n");
